boomBoomCowboy.game = ( function() {

	//Setup Box2D vars
	var b2Vec2 = Box2D.Common.Math.b2Vec2;
	var b2BodyDef = Box2D.Dynamics.b2BodyDef;
	var b2Body = Box2D.Dynamics.b2Body;
	var b2FixtureDef = Box2D.Dynamics.b2FixtureDef;
	var b2Fixture = Box2D.Dynamics.b2Fixture;
	var b2World = Box2D.Dynamics.b2World;
	var b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape;
	var b2CircleShape = Box2D.Collision.Shapes.b2CircleShape;
	var b2DebugDraw = Box2D.Dynamics.b2DebugDraw;

	//Game Vars
	var gameCanvas, gameContext, debugCanvas, debugContext;
	var isFocused = true;
	var levelEnd = "Level End";
	levelEnd.x1 = 0;
	levelEnd.x2 = 0;
	levelEnd.y1 = 0;
	levelEnd.y2 = 0;
	var spriteSheet = new createjs.SpriteSheet(
	{
		images: ["images/TNT2.png"],
		frames: {width: 266, height: 240, regX: 133, regY: 120},
		animations: {
			splode: [0, 7, "splode"]
		}
	});
	
	//TODO: method to toggle debug mode
	//$('#debug').on('click', function() { $('#debugCanvas').toggle(); });

	//Call once page has loaded
	//Handles the creation of vital page elements
	function init() {
		console.log("onload Test ", setup);
		setup.canvas();
		setup.ticker();
		box2d.setup();
		window.onfocus = onFocus;
		window.onblur = onBlur;
	}

	//Handles game being focused
	function onFocus()
	{
		isFocused = true;
		box2d.pauseResume( false );
	}

	//Handles game losing focus
	function onBlur()
	{
		isFocused = false;
		box2d.pauseResume( true );
	}

	function boardOnClick( e ) {

	    var cursorPos = getCursorPosition( e );
	    var playerX = box2d.getPlayerData().x;
	    var playerY = box2d.getPlayerData().y;
	    var playerBody = box2d.getPlayerData().body;

	    var clickDist = calculateDistance( cursorPos.x, cursorPos.y, playerX, playerY );

	    explosion.spawn( cursorPos.x, cursorPos.y );

	    console.log("" + clickDist);
	    if(clickDist < 500){
	      var dX = (playerX - cursorPos.x);
	      var dY = (playerY - cursorPos.y); 
	      var force = 100 / (clickDist * .05 + 1);
	  	  playerBody.ApplyImpulse(new b2Vec2(force*dX/(clickDist+.01),force*dY/(clickDist+.01)),new b2Vec2(playerX,playerY));

	    }
	    
	}

	function getCursorPosition( e ) {
	    var x;
	    var y;
	    if ( e.pageX || e.pageY ) {
	    	x = e.pageX;
	    	y = e.pageY;
	    }
	    else {
	    	x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
	    	y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
	    }
	   	x -= gameCanvas.offsetLeft;
		y -= gameCanvas.offsetTop;

		return {
			x: x,
			y: y
		}
	}

	function calculateDistance( x1, y1, x2, y2 )
	{
		var xCalc = x2 - x1;
		var yCalc = y2 - y1;
		var powAdd = Math.pow( xCalc, 2 ) + Math.pow( yCalc, 2);

		var distance = Math.floor( Math.sqrt( powAdd ) );

		return distance;
	}

	//Creates the gameCanvas and related context, and creates the "ticker" render loop
	var setup = ( function() {

		//Sets up the canvas
		var canvas = function() {
			//Set up game canvas
			gameCanvas = document.getElementById( 'gameCanvas' );
			debugCanvas = document.getElementById( 'debugCanvas' );
			gameCanvas.addEventListener("click", boardOnClick, false);
			gameContext = gameCanvas.getContext( '2d' );
			debugContext = debugCanvas.getContext( '2d' );

			//Tell Easel which canvas to draw
			stage = new createjs.Stage( gameCanvas );
			stage.snapPixelsEnabled = true;
			stage.addChild( new createjs.Bitmap("images/Space.png") );
		}
		
		var backgroundMusic = new Audio("sound/Homely Tune.ogg");
		backgroundMusic.loop = true;
		backgroundMusic.addEventListener('ended', function() {
	    this.currentTime = 0;
	    this.play();
		}, false);
		backgroundMusic.play();
		document.body.appendChild(backgroundMusic);
		

		//Sets the Easel update preferencees
		var ticker = function() {
			createjs.Ticker.setFPS( 60 );

			//Use requestAnimationFrame (MUCH better than setTimeout)
			createjs.Ticker.useRAF = true;

			//Allow the game to listen for a tick from the game
			createjs.Ticker.addListener( boomBoomCowboy.game );
		}

		return {
			canvas: canvas,
			ticker: ticker
		}

	})();


	//Dispay object for cowboy
	var cowboy = (function() {

		var spawn = function( x, y )
		{
			//TODO: Fill-out const data
			var displayBMP = new createjs.Bitmap("images/CowboySmall.png");
			//Starting position
			displayBMP.x = x;
			displayBMP.y = y;
			//Registration point for image (Center of image)
			displayBMP.regX = 17;
			displayBMP.regY = 25;

			displayBMP.snapToPixel = true;
			displayBMP.mouseEnabled = false;

			stage.addChild( displayBMP );
			box2d.createCowboy( displayBMP );
		}

		return {
			spawn: spawn
		}

	})();


	//Display object for wallPieces
	var wallPiece = (function() {

		var spawn = function( x, y )
		{
			//TODO: Fill-out const data
			var displayBMP = new createjs.Bitmap("images/testwall.png");
			//Starting position
			displayBMP.x = x + 13.5;
			displayBMP.y = y + 13.5;
			//Registration point for image (Center of image)
			displayBMP.regX = 13.5;
			displayBMP.regY = 13.5;

			displayBMP.snapToPixel = true;
			displayBMP.mouseEnabled = false;

			stage.addChild( displayBMP );
			box2d.createWallPiece( displayBMP );
		}

		return {
			spawn: spawn
		}

	})();


	//Display object for levelGoal
	var levelGoal = (function() {

		var spawn = function( x, y )
		{
			//TODO: Fill-out const data
			var displayBMP = new createjs.Bitmap("images/GoalShip.png");
			//Starting position
			displayBMP.x = x;
			displayBMP.y = y;
			//Registration point for image (Center of image)
			displayBMP.regX = 50;
			displayBMP.regY = 36;

			levelEnd.x1 = displayBMP.x - displayBMP.regX;
			levelEnd.x2 = displayBMP.x + displayBMP.regX;
			levelEnd.y1 = displayBMP.y - displayBMP.regY;
			levelEnd.y2 = displayBMP.y + displayBMP.regY;

			//alert( levelEnd.x1 + "   " + levelEnd.x2 + "   " + levelEnd.y1 + "   " + levelEnd.y2 );

			displayBMP.snapToPixel = true;
			displayBMP.mouseEnabled = false;

			stage.addChild( displayBMP );
			
		}

		return {
			spawn: spawn
		}

	})();

	var explosion = (function() {
		var spawn = function( x, y )
		{

			bmpAnimation = new createjs.BitmapAnimation( spriteSheet );
			bmpAnimation.gotoAndPlay("splode");

			bmpAnimation.name = "splody";
			bmpAnimation.x = x;
			bmpAnimation.y = y;
			bmpAnimation.vX = .7;
			bmpAnimation.onAnimationEnd = function( bmp, ani )
			{
				bmpAnimation.stop;
				stage.removeChild( bmpAnimation );
			}

			bmpAnimation.currentFrame = 0;
			stage.addChild( bmpAnimation );

		}

		return {
			spawn: spawn
		}
	})();


	var box2d = (function() {

		var SCALE = 30;
		var STEP = 30;
		var TIMESTAMP = 1 / STEP;

		var world;
		var lastTimestamp = Date.now();
		var fixedTimestepAccumulator = 0;
		var playerCowboy;
		var playerStartX;
		var playerStartY;
		var currentLevel = 0;

		var setup = function() 
		{
			world = new b2World( new b2Vec2( 0, 10 ), true );
			addDebug();

			
			loadLevel(currentLevel);
			cowboy.spawn( playerStartX, playerStartY );
			
		}

		//Loads a json level 
		var loadLevel = function( levelNum ) 
		{
			world = new b2World( new b2Vec2( 0, 10 ), true );
			addDebug();
			//Generate walls and start position for player 
			playerStartX = levelData.levelsData[levelNum].start.x * 26.67;
			playerStartY = levelData.levelsData[levelNum].start.y * 26.67;
			//levelGoal.spawn(levelData.levelsData[levelNum].goal.x * 26.67,levelData.levelsData[levelNum].goal.y * 26.67);
			for (var i=0;i<levelData.levelsData[levelNum].blocks.length;i++){
				var block = levelData.levelsData[levelNum].blocks[i];
				var height = block.height;
				var width = block.width;
				for(var j=0;j<width;j++)
				{
					for(var k=0;k<height;k++)
						wallPiece.spawn( (block.x+j)*26.67, (block.y+k)*26.67 );
				}
			}

			//Spawn the level goal
			levelGoal.spawn( levelData.levelsData[ levelNum ].goal.x * 26.67, levelData.levelsData[ levelNum ].goal.y * 26.67 );
				
		}
		
		
		//Handles what the debug display will look like
		var addDebug = function()
		{
			var debugDraw = new b2DebugDraw();
			debugDraw.SetSprite( debugContext );
			debugDraw.SetDrawScale( SCALE );
			debugDraw.SetFillAlpha( 0.7 );
			debugDraw.SetLineThickness( 1.0 );
			debugDraw.SetFlags( b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit );
			world.SetDebugDraw( debugDraw );
		}

		//gameObject allows easy communication between Easel and Box2D
		var gameObject = function( physBody, dispSkin )
		{
			this.physBody = physBody;
			this.dispSkin = dispSkin;

			//Get the position of the box2d object
			this.update = function()
			{	
				//Convert Box2D positions into pixels
				this.dispSkin.rotation = this.physBody.GetAngle() * ( 180 / Math.PI );
				this.dispSkin.x = this.physBody.GetWorldCenter().x * SCALE;
				this.dispSkin.y = this.physBody.GetWorldCenter().y * SCALE;
			}

			playerCowboy = this;
		}

		//Creates a cowboy instance within box2d
		var createCowboy = function( dispSkin ) {
			var fixtureDef = new b2FixtureDef;
			fixtureDef.density = .5;
			fixtureDef.restitution = .2;
			//fixtureDef.shape = new b2CircleShape( dispSkin.regY / SCALE );
			fixtureDef.shape = new b2PolygonShape;
			fixtureDef.shape.SetAsBox( (dispSkin.regX - 8) / SCALE, dispSkin.regY / SCALE );
			var bodyDef = new b2BodyDef;
			bodyDef.type = b2Body.b2_dynamicBody;
			bodyDef.position.x = dispSkin.x / SCALE;
			bodyDef.position.y = dispSkin.y / SCALE;
			var cowboy = world.CreateBody( bodyDef );
			cowboy.CreateFixture( fixtureDef );
			
			// assign actor
			var gObject = new gameObject( cowboy, dispSkin );
			cowboy.SetUserData( gObject );  // set the actor as user data of the body so we can use it later: body.GetUserData()
		}

		//Creates a wallPiece instance within box2d
		var createWallPiece = function( dispSkin ) {
			var fixtureDef = new b2FixtureDef;
			fixtureDef.shape = new b2PolygonShape;
			fixtureDef.shape.SetAsBox( dispSkin.regX / SCALE, dispSkin.regY / SCALE );
			var bodyDef = new b2BodyDef;
			bodyDef.type = b2Body.b2_staticBody;
			bodyDef.position.x = dispSkin.x / SCALE;
			bodyDef.position.y = dispSkin.y / SCALE;
			var wallPiece = world.CreateBody( bodyDef );
			wallPiece.CreateFixture( fixtureDef );
		}

		var removeGameObject = function( gObject )
		{
			playerCowboy = null;
		}

		function playerIsIntersecting( x1, y1, x2, y2 )
		{
			var pX = getPlayerData.x;
			var pY = getPlayerData.y;
			return ( ( pX >= x1 ) && ( pX <= x2 ) && ( pY >= y1 ) && ( pY <= y2 ) );
		}

		//Update for box2d gameObjects
		var update = function() {
			var now = Date.now();
			var deltaTime = now - lastTimestamp;
			fixedTimestepAccumulator += deltaTime;
			lastTimestamp = now;

			while( fixedTimestepAccumulator >= STEP )
			{
				playerCowboy.update();

				//Is the player intersecting the finish point?
				if( playerIsIntersecting( levelEnd.x1, levelEnd.y1, levelEnd.x2, levelEnd.y2 ) )
				{
					alert( "Level Complete!" );

					if( levelData.levelsData[currentLevel + 1] != null )
					{
						loadLevel( ++currentLevel );
					}
				}

				world.Step( TIMESTAMP, 10, 10 );

				fixedTimestepAccumulator -= STEP;
				world.ClearForces();
				world.m_debugDraw.m_sprite.graphics.clear();
		   		world.DrawDebugData();
			}
		}

		var pauseResume = function(p) {
			if(p) { 
				TIMESTEP = 0;
			} else { 
				TIMESTEP = 1/STEP; 
			}
			lastTimestamp = Date.now();
		}

		function getPlayerData() {
			return {
				x: playerCowboy.dispSkin.x,
				y: playerCowboy.dispSkin.y,
				body: playerCowboy.physBody
			}
		}

		return {
			setup: setup,
			update: update,
			getPlayerData: getPlayerData,
			createCowboy: createCowboy,
			createWallPiece: createWallPiece,
			pauseResume: pauseResume
		}

	})();

	var tick = function(dt, paused) 
	{
		if( isFocused ) {
			box2d.update();
			stage.update();
		}
	}

	return {
		tick : tick, 
		init : init
	}

}());